query("SELECT classid FROM class ORDER BY classid"); $classes = $stmt->fetchAll(PDO::FETCH_ASSOC); $stmt = $DBcon->query("SELECT academic_year FROM calender ORDER BY academic_year DESC"); $academic_years = $stmt->fetchAll(PDO::FETCH_ASSOC); } catch (PDOException $e) { error_log("Database error: " . $e->getMessage()); } // Initialize variables $action = isset($_GET['action']) ? $_GET['action'] : 'list'; $id = isset($_GET['id']) ? intval($_GET['id']) : 0; $message = ''; $message_type = ''; $student = null; $students = []; $current_page = isset($_GET['page']) ? max(1, intval($_GET['page'])) : 1; $per_page = 25; $total_records = 0; $total_pages = 0; // CSRF token generation (if session not started) if (session_status() !== PHP_SESSION_ACTIVE) { session_start(); } if (empty($_SESSION['csrf_token'])) { $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); } $csrf_token = $_SESSION['csrf_token']; // Helper: validate CSRF on POST function check_csrf() { if (session_status() !== PHP_SESSION_ACTIVE) { session_start(); } $posted = $_POST['csrf_token'] ?? ''; if (empty($posted) || !hash_equals($_SESSION['csrf_token'] ?? '', $posted)) { return false; } return true; } // Image handling functions function saveWebcamImage($base64_image) { if (empty($base64_image) || $base64_image === 'data:,') { return ''; } $upload_dir = __DIR__ . '/../uploads/students/'; if (!file_exists($upload_dir)) { mkdir($upload_dir, 0755, true); } if (preg_match('/^data:image\/(\w+);base64,/', $base64_image, $type)) { $image_data = substr($base64_image, strpos($base64_image, ',') + 1); $type = strtolower($type[1]); if (!in_array($type, ['jpg', 'jpeg', 'png', 'gif'])) { return ''; } $image_data = base64_decode($image_data); if ($image_data === false) { return ''; } $filename = 'student_' . uniqid() . '.' . $type; $filepath = $upload_dir . $filename; if (file_put_contents($filepath, $image_data)) { return 'uploads/students/' . $filename; } } return ''; } function saveUploadedImage($file) { if (!isset($file) || $file['error'] !== UPLOAD_ERR_OK) { return ''; } $allowed_types = ['image/jpeg', 'image/jpg', 'image/png', 'image/gif']; $max_size = 2 * 1024 * 1024; if (!in_array($file['type'], $allowed_types) || $file['size'] > $max_size) { return ''; } $upload_dir = __DIR__ . '/../uploads/students/'; if (!file_exists($upload_dir)) { mkdir($upload_dir, 0755, true); } $extension = pathinfo($file['name'], PATHINFO_EXTENSION); $filename = 'student_' . uniqid() . '.' . $extension; $filepath = $upload_dir . $filename; if (move_uploaded_file($file['tmp_name'], $filepath)) { return 'uploads/students/' . $filename; } return ''; } // Handle POST requests (Create, Update, Delete) if ($_SERVER['REQUEST_METHOD'] === 'POST') { // CSRF check if (!check_csrf()) { $message = 'Invalid CSRF token.'; $message_type = 'error'; } else { $post_action = $_POST['action'] ?? ''; if ($post_action === 'create' || $post_action === 'update') { // Basic validation $fullname = trim($_POST['fullname'] ?? ''); $regno = trim($_POST['regno'] ?? ''); $class_id = $_POST['class_id'] ?? ''; $admin_year = $_POST['admin_year'] ?? ''; if (empty($fullname) || empty($regno) || empty($class_id)) { $message = 'Please fill all required fields.'; $message_type = 'error'; } else { try { // Handle image upload $image_path = ''; // Check for webcam image if (!empty($_POST['webcam_image']) && $_POST['webcam_image'] !== 'data:,') { $image_path = saveWebcamImage($_POST['webcam_image']); } // Check for file upload elseif (isset($_FILES['image_upload']) && $_FILES['image_upload']['error'] === UPLOAD_ERR_OK) { $image_path = saveUploadedImage($_FILES['image_upload']); } if ($post_action === 'create') { // CREATE STUDENT $sql = "INSERT INTO students_info (fullname, regno, class_id, admin_year, image) VALUES (:fullname, :regno, :class_id, :admin_year, :image)"; $stmt = $DBcon->prepare($sql); $stmt->bindParam(':fullname', $fullname); $stmt->bindParam(':regno', $regno); $stmt->bindParam(':class_id', $class_id); $stmt->bindParam(':admin_year', $admin_year); $stmt->bindParam(':image', $image_path); if ($stmt->execute()) { $message = 'Student created successfully.'; $message_type = 'success'; $action = 'list'; // Also insert into promoted table $promoted_sql = "INSERT INTO promoted (regno, accademic_year, class_id) VALUES (:regno, :year, :class)"; $promoted_stmt = $DBcon->prepare($promoted_sql); $promoted_stmt->execute([ ':regno' => $regno, ':year' => $admin_year, ':class' => $class_id ]); } else { $message = 'Failed to create student.'; $message_type = 'error'; } } elseif ($post_action === 'update') { // UPDATE STUDENT $student_id = intval($_POST['id'] ?? 0); if ($student_id > 0) { // Get current student data $current_stmt = $DBcon->prepare("SELECT * FROM students_info WHERE sn = :id"); $current_stmt->execute([':id' => $student_id]); $current_student = $current_stmt->fetch(PDO::FETCH_ASSOC); if ($current_student) { // If no new image, keep the old one if (empty($image_path)) { $image_path = $current_student['image']; } else { // Delete old image if exists if (!empty($current_student['image']) && file_exists(__DIR__ . '/../' . $current_student['image'])) { @unlink(__DIR__ . '/../' . $current_student['image']); } } $sql = "UPDATE students_info SET fullname = :fullname, regno = :regno, class_id = :class_id, admin_year = :admin_year, image = :image WHERE sn = :id"; $stmt = $DBcon->prepare($sql); $stmt->bindParam(':fullname', $fullname); $stmt->bindParam(':regno', $regno); $stmt->bindParam(':class_id', $class_id); $stmt->bindParam(':admin_year', $admin_year); $stmt->bindParam(':image', $image_path); $stmt->bindParam(':id', $student_id, PDO::PARAM_INT); if ($stmt->execute()) { $message = 'Student updated successfully.'; $message_type = 'success'; $action = 'list'; // Update promoted table $promoted_sql = "UPDATE promoted SET regno = :new_regno, accademic_year = :year, class_id = :class WHERE regno = :old_regno"; $promoted_stmt = $DBcon->prepare($promoted_sql); $promoted_stmt->execute([ ':new_regno' => $regno, ':year' => $admin_year, ':class' => $class_id, ':old_regno' => $current_student['regno'] ]); } else { $message = 'Failed to update student.'; $message_type = 'error'; } } else { $message = 'Student not found.'; $message_type = 'error'; $action = 'list'; } } else { $message = 'Invalid student ID.'; $message_type = 'error'; } } } catch (PDOException $e) { error_log("Database error: " . $e->getMessage()); $message = 'Database error occurred.'; $message_type = 'error'; } } } elseif ($post_action === 'delete') { // DELETE SINGLE STUDENT $student_id = intval($_POST['id'] ?? 0); if ($student_id > 0) { try { // Get student info first $stmt = $DBcon->prepare("SELECT regno, image FROM students_info WHERE sn = :id"); $stmt->execute([':id' => $student_id]); $student_data = $stmt->fetch(PDO::FETCH_ASSOC); if ($student_data) { // Delete from promoted table $promoted_stmt = $DBcon->prepare("DELETE FROM promoted WHERE regno = :regno"); $promoted_stmt->execute([':regno' => $student_data['regno']]); // Delete image if exists if (!empty($student_data['image']) && file_exists(__DIR__ . '/../' . $student_data['image'])) { @unlink(__DIR__ . '/../' . $student_data['image']); } // Delete student $delete_stmt = $DBcon->prepare("DELETE FROM students_info WHERE sn = :id"); if ($delete_stmt->execute([':id' => $student_id])) { $message = 'Student deleted successfully.'; $message_type = 'success'; } } } catch (PDOException $e) { error_log("Delete error: " . $e->getMessage()); $message = 'Failed to delete student.'; $message_type = 'error'; } } } elseif ($post_action === 'bulk_delete') { // BULK DELETE if (isset($_POST['selected_students']) && is_array($_POST['selected_students'])) { $success_count = 0; foreach ($_POST['selected_students'] as $student_id_raw) { $student_id = intval($student_id_raw); if ($student_id > 0) { try { // Similar to single delete logic $stmt = $DBcon->prepare("SELECT regno, image FROM students_info WHERE sn = :id"); $stmt->execute([':id' => $student_id]); $student_data = $stmt->fetch(PDO::FETCH_ASSOC); if ($student_data) { // Delete from promoted table $promoted_stmt = $DBcon->prepare("DELETE FROM promoted WHERE regno = :regno"); $promoted_stmt->execute([':regno' => $student_data['regno']]); // Delete image if (!empty($student_data['image']) && file_exists(__DIR__ . '/../' . $student_data['image'])) { @unlink(__DIR__ . '/../' . $student_data['image']); } // Delete student $delete_stmt = $DBcon->prepare("DELETE FROM students_info WHERE sn = :id"); if ($delete_stmt->execute([':id' => $student_id])) { $success_count++; } } } catch (PDOException $e) { error_log("Bulk delete error: " . $e->getMessage()); } } } if ($success_count > 0) { $message = "Successfully deleted $success_count student(s)."; $message_type = 'success'; } else { $message = 'No students were deleted.'; $message_type = 'error'; } } } } } // Handle Excel Export (CSV) - MODIFIED TO SORT ALPHABETICALLY if (isset($_GET['export']) && $_GET['export'] === 'excel') { try { $filter_class = $_GET['filter_class'] ?? ''; $filter_year = $_GET['filter_year'] ?? ''; $sql = "SELECT * FROM students_info WHERE 1=1"; $params = []; if (!empty($filter_class)) { $sql .= " AND class_id = :class_id"; $params[':class_id'] = $filter_class; } if (!empty($filter_year)) { $sql .= " AND admin_year = :admin_year"; $params[':admin_year'] = $filter_year; } // MODIFIED: Order by fullname alphabetically (A-Z) instead of sn DESC $sql .= " ORDER BY fullname ASC"; $stmt = $DBcon->prepare($sql); $stmt->execute($params); $students = $stmt->fetchAll(PDO::FETCH_ASSOC); // Generate CSV header('Content-Type: text/csv'); header('Content-Disposition: attachment;filename="students_export_' . date('Y-m-d_H-i-s') . '.csv"'); $output = fopen('php://output', 'w'); // CSV header row fputcsv($output, ['SN', 'Full Name', 'Registration Number', 'Class', 'Admission Year', 'Image Path']); foreach ($students as $student) { fputcsv($output, [ $student['sn'], $student['fullname'], $student['regno'], $student['class_id'], $student['admin_year'], $student['image'] ]); } fclose($output); exit; } catch (PDOException $e) { error_log("Export error: " . $e->getMessage()); } } // Get student for editing if ($action === 'edit' && $id > 0) { try { $stmt = $DBcon->prepare("SELECT * FROM students_info WHERE sn = :id"); $stmt->execute([':id' => $id]); $student = $stmt->fetch(PDO::FETCH_ASSOC); if (!$student) { $message = 'Student not found.'; $message_type = 'error'; $action = 'list'; } } catch (PDOException $e) { error_log("Get student error: " . $e->getMessage()); $action = 'list'; } } // Get students for listing (with filters and pagination) if ($action === 'list') { try { $filter_class = $_GET['filter_class'] ?? ''; $filter_year = $_GET['filter_year'] ?? ''; // Build query with filters $sql = "SELECT * FROM students_info WHERE 1=1"; $count_sql = "SELECT COUNT(*) as total FROM students_info WHERE 1=1"; $params = []; $count_params = []; if (!empty($filter_class)) { $sql .= " AND class_id = :class_id"; $count_sql .= " AND class_id = :class_id"; $params[':class_id'] = $filter_class; $count_params[':class_id'] = $filter_class; } if (!empty($filter_year)) { $sql .= " AND admin_year = :admin_year"; $count_sql .= " AND admin_year = :admin_year"; $params[':admin_year'] = $filter_year; $count_params[':admin_year'] = $filter_year; } $sql .= " ORDER BY sn DESC LIMIT :offset, :per_page"; // Get total count $count_stmt = $DBcon->prepare($count_sql); $count_stmt->execute($count_params); $total_result = $count_stmt->fetch(PDO::FETCH_ASSOC); $total_records = $total_result['total'] ?? 0; $total_pages = ceil($total_records / $per_page); // Get paginated results $offset = ($current_page - 1) * $per_page; $stmt = $DBcon->prepare($sql); foreach ($params as $key => $value) { $stmt->bindValue($key, $value); } $stmt->bindValue(':offset', $offset, PDO::PARAM_INT); $stmt->bindValue(':per_page', $per_page, PDO::PARAM_INT); $stmt->execute(); $students = $stmt->fetchAll(PDO::FETCH_ASSOC); } catch (PDOException $e) { error_log("List students error: " . $e->getMessage()); $students = []; } } ?>